#include "Sphere.h"
#include <iostream>

using namespace std;

Symbol WSphere::parse(ifstream &file, Symbol curToken) {
    float *numFill;

    curToken = matchToken(file, curToken, LBrace);

    numFill = parseVector(file, curToken, &curToken, 3);
    center = vec3(numFill[0], numFill[1], numFill[2]);
    delete numFill;

    curToken = matchToken(file, curToken, Comma);
    if (curToken.isToken) {
        cout << "expected number, found " 
            << tokenList[curToken.t] << endl;
        exit(1);
    }

    radius = curToken.num;
    curToken = nextToken(file);

    curToken = parseMaterials(file, curToken, this);
    return curToken;
}

bool WSphere::intersect(Ray *r) {
    vec3 PtoC, point, d;
    vec4 pprime, dprime;
    float A;
    float B;
    float C;
    float t, t1, t2;
    float deter;

    pprime = transform * vec4(r->point.x, r->point.y, r->point.z, 1.0);
    dprime = transform * vec4(r->d.x, r->d.y, r->d.z, 0.0);
    point = vec3(pprime.x, pprime.y, pprime.z);
    d = vec3(dprime.x, dprime.y, dprime.z);

    PtoC = point - center;
    A = dot(d, d);
    B = 2 * dot(d, PtoC);
    C = dot(PtoC, PtoC) - radius * radius;

    deter = B * B - 4 * A * C;

    if (deter < 0) {
        return false;
    }

    t1 = (-B + sqrt(deter))/(2 * A);
    t2 = (-B - sqrt(deter))/(2 * A);
    if (t2 > 0.001 && t2 < t1) {
        t = t2;
    }
    else {
        t = t1;
    }

    if (t > 0 && t < r->t) {
        r->t = t;
        return true;
    }

    return false;
}

vec3 WSphere::getNormal(Ray *r) {
    vec3 point, d, n;
    vec4 pprime, dprime, normal;

    pprime = transform * vec4(r->point.x, r->point.y, r->point.z, 1.0);
    dprime = transform * vec4(r->d.x, r->d.y, r->d.z, 0.0);
    point = vec3(pprime.x, pprime.y, pprime.z);
    d = vec3(dprime.x, dprime.y, dprime.z);

    n = point + r->t * d - center;
    normal = transpose(transform) * vec4(n.x, n.y, n.z, 0.0);

    return normalize(vec3(normal.x, normal.y, normal.z));
}

AABB *WSphere::generateAABB() {
    AABB *ret;

    ret = new AABB();
    ret->g = this;
    ret->isLeaf = true;

    ret->min = center - vec3(radius, radius, radius);
    ret->max = center + vec3(radius, radius, radius);

    fix_AABB_transform(ret, vtrans);

    return ret;
}
